#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <signal.h>
#include <time.h>
#include <sys/select.h>
#include <sys/socket.h>

#define BUFFER_SIZE 1024
#define DEFAULT_DEVICE "/dev/ttyV0"

volatile int running = 1;

void handle_sigint(int sig) {
    running = 0;
    printf("\n正在关闭串口测试程序...\n");
    fflush(stdout);
}

void get_current_time(char *time_str) {
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);
    strftime(time_str, 26, "%Y-%m-%d %H:%M:%S", tm_info);
}

// 设置串口参数
int setup_serial_port(int fd, int baud_rate) {
    struct termios tty;
    
    // 获取当前串口设置
    if (tcgetattr(fd, &tty) != 0) {
        perror("获取串口属性失败");
        return -1;
    }
    
    // 设置波特率
    cfsetospeed(&tty, baud_rate);
    cfsetispeed(&tty, baud_rate);
    
    // 设置数据位、停止位、校验位
    tty.c_cflag &= ~PARENB;  // 无奇偶校验
    tty.c_cflag &= ~CSTOPB;  // 1个停止位
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;      // 8位数据位
    tty.c_cflag &= ~CRTSCTS; // 无硬件流控
    tty.c_cflag |= CREAD | CLOCAL; // 启用接收，忽略控制线
    
    // 设置输入模式
    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 无软件流控
    tty.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始模式
    
    // 设置输出模式
    tty.c_oflag &= ~OPOST; // 原始输出
    
    // 设置本地模式
    tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 非规范模式
    
    // 设置超时
    tty.c_cc[VMIN] = 0;   // 非阻塞
    tty.c_cc[VTIME] = 1;  // 100ms超时
    
    // 应用设置
    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        perror("设置串口属性失败");
        return -1;
    }
    
    return 0;
}

// 显示数据（十六进制和ASCII）
void display_data(const char *data, int length, const char *prefix) {
    printf("%s (%d字节): ", prefix, length);
    
    // 显示十六进制
    printf("HEX: ");
    for (int i = 0; i < length; i++) {
        printf("%02X ", (unsigned char)data[i]);
    }
    
    // 显示ASCII
    printf(" | ASCII: ");
    for (int i = 0; i < length; i++) {
        if (data[i] >= 32 && data[i] <= 126) {
            printf("%c", data[i]);
        } else {
            printf(".");
        }
    }
    printf("\n");
}

int main(int argc, char *argv[]) {
    int serial_fd;
    char device_name[256] = DEFAULT_DEVICE;
    int baud_rate = B9600;
    char input_buffer[BUFFER_SIZE];
    char read_buffer[BUFFER_SIZE];
    char time_str[26];
    fd_set readfds;
    struct timeval timeout;
    
    // 解析命令行参数
    if (argc > 1) {
        strcpy(device_name, argv[1]);
    }
    if (argc > 2) {
        int rate = atoi(argv[2]);
        switch (rate) {
            case 9600: baud_rate = B9600; break;
            case 19200: baud_rate = B19200; break;
            case 38400: baud_rate = B38400; break;
            case 57600: baud_rate = B57600; break;
            case 115200: baud_rate = B115200; break;
            default: baud_rate = B9600; break;
        }
    }
    
    // 设置信号处理
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handle_sigint;
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);
    
    // 打开串口设备
    serial_fd = open(device_name, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (serial_fd < 0) {
        perror("打开串口设备失败");
        printf("请确保设备 %s 存在且有访问权限\n", device_name);
        return 1;
    }
    
    // 设置串口参数
    if (setup_serial_port(serial_fd, baud_rate) < 0) {
        close(serial_fd);
        return 1;
    }
    
    get_current_time(time_str);
    printf("[%s] 串口测试程序启动成功\n", time_str);
    printf("设备: %s\n", device_name);
    printf("波特率: %d\n", baud_rate);
    printf("输入 'quit' 退出程序\n");
    printf("输入 'help' 查看帮助\n");
    printf("----------------------------------------\n");
    
    while (running) {
        printf("\n请输入要发送的数据: ");
        fflush(stdout);
        
        // 等待用户输入（非阻塞）
        printf("\n请输入要发送的数据: ");
        fflush(stdout);
        
        // 使用select来检查stdin是否有数据，同时检查running标志
        fd_set readfds_input;
        struct timeval timeout_input;
        FD_ZERO(&readfds_input);
        FD_SET(STDIN_FILENO, &readfds_input);
        timeout_input.tv_sec = 1;  // 1秒超时
        timeout_input.tv_usec = 0;
        
        int select_result_input = select(STDIN_FILENO + 1, &readfds_input, NULL, NULL, &timeout_input);
        
        if (select_result_input > 0 && FD_ISSET(STDIN_FILENO, &readfds_input)) {
            if (fgets(input_buffer, BUFFER_SIZE, stdin) == NULL) {
                break;
            }
        } else if (select_result_input < 0) {
            perror("select stdin失败");
            break;
        } else {
            // 超时，检查running标志
            if (!running) {
                break;
            }
            continue;  // 继续循环
        }
        
        // 去除换行符
        input_buffer[strcspn(input_buffer, "\n")] = '\0';
        
        // 检查特殊命令
        if (strcmp(input_buffer, "quit") == 0) {
            printf("正在退出...\n");
            break;
        }
        
        if (strcmp(input_buffer, "help") == 0) {
            printf("帮助信息:\n");
            printf("  quit - 退出程序\n");
            printf("  help - 显示此帮助\n");
            printf("  clear - 清空接收缓冲区\n");
            printf("  其他任何输入都会作为数据发送到串口\n");
            continue;
        }
        
        if (strcmp(input_buffer, "clear") == 0) {
            tcflush(serial_fd, TCIOFLUSH);
            printf("接收缓冲区已清空\n");
            continue;
        }
        
        // 发送数据
        int write_len = strlen(input_buffer);
        if (write_len > 0) {
            get_current_time(time_str);
            printf("[%s] 发送数据: ", time_str);
            display_data(input_buffer, write_len, "发送");
            
            int bytes_written = write(serial_fd, input_buffer, write_len);
            if (bytes_written < 0) {
                perror("写入串口失败");
            } else if (bytes_written != write_len) {
                printf("警告: 只写入了 %d/%d 字节\n", bytes_written, write_len);
            }
        }
        
        // 等待并读取响应
        printf("等待响应...\n");
        
        timeout.tv_sec = 3;  // 3秒超时
        timeout.tv_usec = 0;
        
        FD_ZERO(&readfds);
        FD_SET(serial_fd, &readfds);
        
        int select_result = select(serial_fd + 1, &readfds, NULL, NULL, &timeout);
        
        if (select_result > 0 && FD_ISSET(serial_fd, &readfds)) {
            int bytes_read = read(serial_fd, read_buffer, BUFFER_SIZE - 1);
            if (bytes_read > 0) {
                read_buffer[bytes_read] = '\0';
                get_current_time(time_str);
                printf("[%s] 收到响应: ", time_str);
                display_data(read_buffer, bytes_read, "接收");
            } else if (bytes_read < 0) {
                perror("读取串口失败");
            }
        } else if (select_result == 0) {
            get_current_time(time_str);
            printf("[%s] 等待超时，未收到响应\n", time_str);
        } else if (select_result < 0) {
            perror("select失败");
        }
    }
    
    close(serial_fd);
    get_current_time(time_str);
    printf("[%s] 串口测试程序已关闭\n", time_str);
    
    return 0;
} 